"""
有声读物API测试
"""

import pytest
import json
from datetime import datetime
from fastapi.testclient import TestClient
from unittest.mock import Mock, patch, AsyncMock
from app.services.tts import tts_service


class TestAudioBookAPI:
    """有声读物API测试类"""
    
    def test_create_audiobook_success(self, test_client: TestClient, auth_headers: dict, mock_auth):
        """测试创建有声读物成功"""
        data = {
            "title": "测试有声读物",
            "content": "这是测试内容，长度需要超过10个字符。",
            "author": "测试作者",
            "language": "zh-CN",
            "chapter_max_length": 5000,
            "output_format": "mp3",
            "auto_chapters": True,
            "chapter_separator": "\n\n",
            "description": "这是一个测试用的有声读物描述",
            "voice_id": None,
            "voice_preset": None,
            "voice_settings": None
        }
        
        with patch.object(type(tts_service), 'is_initialized', property(lambda self: True)), \
             patch('app.api.v1.audiobook._split_into_chapters') as mock_split, \
             patch('app.services.audiobook.processor.AudioBookProcessor.create_segments') as mock_create, \
             patch('app.services.audiobook.generator.audio_book_generator.submit_task') as mock_submit, \
             patch('app.api.v1.audiobook._generate_audiobook_id') as mock_generate_id, \
             patch('app.api.v1.audiobook._save_audiobook_info') as mock_save_info, \
             patch('app.services.tts.tts_service.get_available_voices') as mock_get_voices, \
             patch('app.services.tts.tts_service.set_voice_preset') as mock_set_preset:
            
            mock_get_voices.return_value = [Mock(id="test_voice_id")]
            mock_set_preset.return_value = True
            
            # 使用正确的章节模型返回值
            from app.api.v1.audiobook import ChapterModel
            mock_split.return_value = [
                ChapterModel(title="第1章", content="这是第1章的内容", order=0),
                ChapterModel(title="第2章", content="这是第2章的内容", order=1)
            ]
            mock_generate_id.return_value = "test_audiobook_id"
            mock_save_info.return_value = {
                "created_at": datetime.utcnow(),
                "updated_at": datetime.utcnow()
            }
            
            mock_create.return_value = AsyncMock(
                title=data["title"],
                author=data["author"],
                language=data["language"],
                total_segments=5,
                estimated_total_duration=120.0
            )
            mock_submit.return_value = "task_123"
            
            response = test_client.post(
                "/api/v1/audiobooks",
                json=data,
                headers=auth_headers
            )
            
            # 打印响应内容用于调试
            print(f"Status Code: {response.status_code}")
            print(f"Request URL: /api/v1/audiobooks")
            print(f"Request JSON: {json.dumps(data, indent=2)}")
            print(f"Response Headers: {response.headers}")
            print(f"Response Raw Content: {response.content.decode('utf-8', errors='replace')}")
            
            try:
                response_json = response.json()
                print(f"Response JSON: {json.dumps(response_json, indent=2)}")
            except json.JSONDecodeError:
                print("Response is not JSON")
            
            # 恢复断言
            assert response.status_code == 202, f"Expected status code 202, got {response.status_code}"
            result = response.json()
            
            # 检查响应内容是否符合预期
            assert "id" in result, "响应中应包含id字段"
            assert result["title"] == data["title"], "标题应与请求中一致"
            assert result["author"] == data["author"], "作者应与请求中一致"
            assert result["status"] == "processing", "状态应为processing"
    
    def test_create_audiobook_validation_error(self, test_client: TestClient, auth_headers: dict):
        """测试创建有声读物验证错误"""
        data = {
            "title": "",  # 空标题
            "content": "这是测试内容"
        }
        
        response = test_client.post(
            "/api/v1/audiobooks",
            json=data,
            headers=auth_headers
        )
        
        assert response.status_code == 422
        result = response.json()
        assert result["error"]["code"] == "VALIDATION_ERROR"
        assert "validation_errors" in result["error"]["details"]
    
    def test_create_audiobook_unauthorized(self, test_client: TestClient):
        """测试创建有声读物未授权"""
        data = {
            "title": "测试有声读物",
            "content": "这是测试内容"
        }
        
        response = test_client.post(
            "/api/v1/audiobooks",
            json=data
        )
        
        assert response.status_code == 401
        result = response.json()
        assert result["error"]["code"] == "HTTP_401"
    
    def test_get_audiobooks_success(self, test_client: TestClient, auth_headers: dict, mock_auth):
        """测试获取有声读物列表成功"""
        with patch('app.repositories.audiobook.AudioBookRepository.get_all') as mock_get_all:
            mock_get_all.return_value = [
                Mock(
                    id="1",
                    title="有声读物1",
                    author="作者1",
                    language="zh-CN",
                    status="completed"
                ),
                Mock(
                    id="2", 
                    title="有声读物2",
                    author="作者2",
                    language="en-US",
                    status="processing"
                )
            ]
            
            response = test_client.get(
                "/api/v1/audiobooks",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert len(result["data"]) == 2
            assert result["data"][0]["title"] == "有声读物1"
    
    def test_get_audiobook_by_id_success(
        self, 
        test_client: TestClient, 
        auth_headers: dict,
        sample_audiobook_data: dict,
        mock_auth
    ):
        """测试根据ID获取有声读物成功"""
        audiobook = sample_audiobook_data["audiobook"]
        
        with patch('app.repositories.audiobook.AudioBookRepository.get') as mock_get:
            mock_get.return_value = audiobook
            
            response = test_client.get(
                f"/api/v1/audiobooks/{audiobook.id}",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert result["data"]["id"] == audiobook.id
            assert result["data"]["title"] == audiobook.title
    
    def test_get_audiobook_by_id_not_found(self, test_client: TestClient, auth_headers: dict, mock_auth):
        """测试根据ID获取有声读物未找到"""
        with patch('app.repositories.audiobook.AudioBookRepository.get') as mock_get:
            mock_get.return_value = None
            
            response = test_client.get(
                "/api/v1/audiobooks/nonexistent",
                headers=auth_headers
            )
            
            assert response.status_code == 404
            result = response.json()
            assert result["error"]["code"] == "NOT_FOUND"
    
    def test_update_audiobook_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试更新有声读物成功"""
        audiobook = sample_audiobook_data["audiobook"]
        update_data = {
            "title": "更新后的标题",
            "description": "更新后的描述"
        }
        
        with patch('app.repositories.audiobook.AudioBookRepository.get') as mock_get:
            mock_get.return_value = audiobook
            
            with patch('app.repositories.audiobook.AudioBookRepository.update') as mock_update:
                updated_audiobook = Mock(
                    id=audiobook.id,
                    title=update_data["title"],
                    description=update_data["description"],
                    author=audiobook.author,
                    language=audiobook.language
                )
                mock_update.return_value = updated_audiobook
                
                response = test_client.put(
                    f"/api/v1/audiobooks/{audiobook.id}",
                    json=update_data,
                    headers=auth_headers
                )
                
                assert response.status_code == 200
                result = response.json()
                assert result["success"] is True
                assert result["data"]["title"] == update_data["title"]
    
    def test_delete_audiobook_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试删除有声读物成功"""
        audiobook = sample_audiobook_data["audiobook"]
        
        with patch('app.repositories.audiobook.AudioBookRepository.get') as mock_get:
            mock_get.return_value = audiobook
            
            with patch('app.repositories.audiobook.AudioBookRepository.remove') as mock_remove:
                mock_remove.return_value = True
                
                response = test_client.delete(
                    f"/api/v1/audiobooks/{audiobook.id}",
                    headers=auth_headers
                )
                
                assert response.status_code == 200
                result = response.json()
                assert result["success"] is True
    
    def test_get_audiobook_chapters_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试获取有声读物章节成功"""
        audiobook = sample_audiobook_data["audiobook"]
        chapters = sample_audiobook_data["chapters"]
        
        with patch('app.repositories.audiobook.AudioBookChapterRepository.get_by_audiobook') as mock_get_chapters:
            mock_get_chapters.return_value = chapters
            
            response = test_client.get(
                f"/api/v1/audiobooks/{audiobook.id}/chapters",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert len(result["data"]) == len(chapters)
            assert result["data"][0]["title"] == chapters[0].title
    
    def test_get_audiobook_progress_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试获取有声读物进度成功"""
        audiobook = sample_audiobook_data["audiobook"]
        
        with patch('app.services.audiobook.progress_tracker.progress_tracker.get_progress') as mock_get_progress:
            mock_progress = Mock(
                current_chapter=1,
                current_position=30.5,
                total_progress=0.5,
                total_read_time=60.0
            )
            mock_get_progress.return_value = mock_progress
            
            response = test_client.get(
                f"/api/v1/audiobooks/{audiobook.id}/progress",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert result["data"]["current_chapter"] == 1
            assert result["data"]["total_progress"] == 0.5
    
    def test_update_audiobook_progress_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试更新有声读物进度成功"""
        audiobook = sample_audiobook_data["audiobook"]
        progress_data = {
            "chapter_index": 2,
            "position": 45.0
        }
        
        with patch('app.services.audiobook.progress_tracker.progress_tracker.update_progress') as mock_update:
            mock_progress = Mock(
                current_chapter=2,
                current_position=45.0,
                total_progress=0.7
            )
            mock_update.return_value = mock_progress
            
            response = test_client.put(
                f"/api/v1/audiobooks/{audiobook.id}/progress",
                json=progress_data,
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert result["data"]["current_chapter"] == 2
    
    def test_add_bookmark_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试添加书签成功"""
        audiobook = sample_audiobook_data["audiobook"]
        bookmark_data = {
            "chapter_index": 1,
            "position": 25.0,
            "title": "重要位置",
            "description": "这是一个重要的位置"
        }
        
        with patch('app.services.audiobook.progress_tracker.progress_tracker.add_bookmark') as mock_add_bookmark:
            mock_bookmark = Mock(
                id="bookmark_123",
                chapter_index=1,
                position=25.0,
                title=bookmark_data["title"],
                description=bookmark_data["description"]
            )
            mock_add_bookmark.return_value = mock_bookmark
            
            response = test_client.post(
                f"/api/v1/audiobooks/{audiobook.id}/bookmarks",
                json=bookmark_data,
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert result["data"]["title"] == bookmark_data["title"]
    
    def test_get_bookmarks_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict
    ):
        """测试获取书签列表成功"""
        audiobook = sample_audiobook_data["audiobook"]
        
        with patch('app.repositories.audiobook.AudioBookProgressRepository.get_user_progress') as mock_get_progress:
            mock_progress = Mock(
                bookmarks=[
                    {
                        "id": "bookmark_1",
                        "title": "书签1",
                        "chapter_index": 1,
                        "position": 25.0
                    },
                    {
                        "id": "bookmark_2", 
                        "title": "书签2",
                        "chapter_index": 2,
                        "position": 50.0
                    }
                ]
            )
            mock_get_progress.return_value = mock_progress
            
            response = test_client.get(
                f"/api/v1/audiobooks/{audiobook.id}/bookmarks",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert len(result["data"]) == 2
    
    def test_start_playback_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict,
        mock_auth
    ):
        """测试开始播放成功"""
        audiobook = sample_audiobook_data["audiobook"]
        
        with patch('app.services.audiobook.progress_tracker.progress_tracker.start_playback') as mock_start:
            mock_session = Mock(
                id="session_123",
                audiobook_id=audiobook.id,
                user_id="test_user",
                current_position=0.0
            )
            mock_start.return_value = mock_session
            
            response = test_client.post(
                f"/api/v1/audiobooks/{audiobook.id}/play",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
            assert result["data"]["session_id"] == "session_123"
    
    def test_pause_playback_success(
        self,
        test_client: TestClient,
        auth_headers: dict,
        sample_audiobook_data: dict,
        mock_auth
    ):
        """测试暂停播放成功"""
        audiobook = sample_audiobook_data["audiobook"]
        
        with patch('app.services.audiobook.progress_tracker.progress_tracker.pause_playback') as mock_pause:
            mock_progress = Mock(
                current_position=30.0,
                total_read_time=60.0
            )
            mock_pause.return_value = mock_progress
            
            response = test_client.post(
                f"/api/v1/audiobooks/{audiobook.id}/pause",
                headers=auth_headers
            )
            
            assert response.status_code == 200
            result = response.json()
            assert result["success"] is True
    
    @pytest.mark.slow
    def test_audiobook_performance(self, test_client: TestClient, auth_headers: dict, mock_auth):
        """测试有声读物API性能"""
        import time
        
        # 测试获取列表性能
        start_time = time.time()
        
        with patch('app.repositories.audiobook.AudioBookRepository.get_all') as mock_get_all:
            mock_get_all.return_value = [Mock() for _ in range(100)]  # 模拟100条记录
            
            response = test_client.get(
                "/api/v1/audiobooks",
                headers=auth_headers
            )
            
            end_time = time.time()
            response_time = end_time - start_time
            
            assert response.status_code == 200
            assert response_time < 1.0  # 响应时间应小于1秒
    
    @pytest.mark.integration
    def test_audiobook_workflow(self, test_client: TestClient, auth_headers: dict, mock_auth):
        """测试有声读物完整工作流"""
        # 1. 创建有声读物
        create_data = {
            "title": "工作流测试",
            "content": "这是完整工作流测试内容",
            "author": "测试作者"
        }
        
        with patch('app.services.audiobook.processor.audio_book_processor.create_segments') as mock_create:
            mock_create.return_value = AsyncMock(
                title=create_data["title"],
                author=create_data["author"],
                language="zh-CN",
                total_segments=3,
                estimated_total_duration=60.0
            )
            
            with patch('app.services.audiobook.generator.audio_book_generator.submit_task') as mock_submit:
                mock_submit.return_value = "task_workflow"
                
                create_response = test_client.post(
                    "/api/v1/audiobooks",
                    json=create_data,
                    headers=auth_headers
                )
                
                assert create_response.status_code == 202
                task_id = create_response.json()["task_id"]
                assert task_id == "task_workflow"
        
        # 2. 检查任务状态
        with patch('app.services.audiobook.generator.audio_book_generator.get_task_status') as mock_status:
            mock_status.return_value = {
                "id": task_id,
                "status": "completed",
                "progress": 1.0,
                "audiobook_id": "audiobook_workflow"
            }
            
            status_response = test_client.get(
                f"/api/v1/tasks/{task_id}",
                headers=auth_headers
            )
            
            assert status_response.status_code == 200
            assert status_response.json()["data"]["status"] == "completed"


if __name__ == "__main__":
    pytest.main([__file__])
